package qy.qyalgotrader.technical;

import java.time.LocalTime;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;

import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
import qy.jalgotrade.dataseries.SequenceDataSeries;
import qy.jalgotrade.event.EventHandler;
import qy.jalgotrade.utils.CommonUtils;

/**
 * <pre>
 * 指定时间 (开盘) 以来历史行情序列 (由构造方法的 dataSeries 参数指定) 的统计量
 * </pre>
 * 
 * <pre>
 * 1. 样本: 时间段: [从 baseTm 开始, 到最近一个 bar time 结束]; 交易日: 近 period 个交易日
 * (若 inclCurrDay 为 True, 则计算 period 包含当前交易日);
 * </pre>
 * 
 * <pre>
 * 2. 使用 pandas.DataFrame 计算:
 * </pre>
 * 
 * @author qy
 *
 */
public class IntraDayMvModel implements EventHandler {

	/**
	 * 开盘 bar 时间
	 */
	private final LocalTime __baseTime;

	/**
	 * 样本交易日数
	 */
	private final int __period;

	/**
	 * 当前交易日是否算在 self.__period
	 */
	private final boolean __inclCurrDay;

	/**
	 * 
	 */
	private SequenceDataSeries<Double> __dataSeries;

	// 均值, 标准差, n 倍标准差轨道:
	/**
	 * 
	 */
	private final double __numStdDev1;

	/**
	 * 
	 */
	private final double __numStdDev2;

	/**
	 * 
	 */
	private final double __numStdDev3;

	/**
	 * 
	 */
	private final SequenceDataSeries<Double> __mean;

	/**
	 * 
	 */
	private final SequenceDataSeries<Double> __stdDev;

	/**
	 * 
	 */
	private final SequenceDataSeries<Double> __upperBand1;

	/**
	 * 
	 */
	private final SequenceDataSeries<Double> __lowerBand1;

	/**
	 * 
	 */
	private final SequenceDataSeries<Double> __upperBand2;

	/**
	 * 
	 */
	private final SequenceDataSeries<Double> __lowerBand2;

	/**
	 * 
	 */
	private final SequenceDataSeries<Double> __upperBand3;

	/**
	 * 
	 */
	private final SequenceDataSeries<Double> __lowerBand3;

	/**
	 * 
	 * @param dataSeries dataSeries
	 * @param period period
	 * @param baseTm baseTm
	 * @param inclCurrDayP default: false
	 * @param numStdDev1P default: 1.0
	 * @param numStdDev2P default: 2.0
	 * @param numStdDev3P default: 3.0
	 * @param maxLenP  default: 0
	 * @throws Exception
	 */
	public IntraDayMvModel(SequenceDataSeries<Double> dataSeries, int period, LocalTime baseTm, Optional<Boolean> inclCurrDayP,
			OptionalDouble numStdDev1P, OptionalDouble numStdDev2P, OptionalDouble numStdDev3P, OptionalInt maxLenP) throws Exception {

		this(dataSeries, period, baseTm, inclCurrDayP.orElse(false),
				numStdDev1P.orElse(1.0), numStdDev2P.orElse(2.0), numStdDev3P.orElse(3.0),maxLenP.orElse(0));
	}

	/**
	 * 
	 * @param  dataSeries dataSeries
	 * @param  period period
	 * @param  baseTm baseTm
	 * @param  inclCurrDay inclCurrDay
	 * @param  numStdDev1 numStdDev1
	 * @param  numStdDev2 numStdDev2
	 * @param  numStdDev3 numStdDev3
	 * @param  maxLen maxLen
	 * @throws Exception
	 */
	public IntraDayMvModel(SequenceDataSeries<Double> dataSeries, int period, LocalTime baseTm, boolean inclCurrDay,
			double numStdDev1, double numStdDev2, double numStdDev3, int maxLen) throws Exception {

		if (inclCurrDay) {
			assert period >= 2;
		} else {
			assert period >= 1;
		}

		__baseTime = baseTm;
		__period = period;
		__inclCurrDay = inclCurrDay;
		__dataSeries = dataSeries;
		// 均值, 标准差, n 倍标准差轨道:
		__numStdDev1 = numStdDev1;
		__numStdDev2 = numStdDev2;
		__numStdDev3 = numStdDev3;
		__mean = new SequenceDataSeries<>(maxLen, Double.class);
		__stdDev = new SequenceDataSeries<>(maxLen, Double.class);
		__upperBand1 = new SequenceDataSeries<>(maxLen, Double.class);
		__lowerBand1 = new SequenceDataSeries<>(maxLen, Double.class);
		__upperBand2 = new SequenceDataSeries<>(maxLen, Double.class);
		__lowerBand2 = new SequenceDataSeries<>(maxLen, Double.class);
		__upperBand3 = new SequenceDataSeries<>(maxLen, Double.class);
		__lowerBand3 = new SequenceDataSeries<>(maxLen, Double.class);
		// 源 dataSeries 的 new-value-event 触发 IntraDayMvModel 更新:
		__dataSeries.getNewValueEvent().subscribe(this);
	}

	@Override
	public void handle(String eventName, Map<String, Object> params) throws Exception {

		@SuppressWarnings("unchecked")
		SequenceDataSeries<Double> dataSeries = (SequenceDataSeries<Double>) params.get("dataSeries");
		ZonedDateTime dateTime = (ZonedDateTime) params.get("dateTime");
		double value = (double) params.get("value");
		__onNewValue(dataSeries, dateTime, value);
	}

	private void __onNewValue(SequenceDataSeries<Double> dataSeries, ZonedDateTime dateTime, double value)
			throws Exception {

		// 仅当样本数据齐全时, 进行计算:
		List<ZonedDateTime> allSampleDms = new ArrayList<>();
		DoubleArrayList allSampleValues = new DoubleArrayList();
		List<ZonedDateTime> allDms = dataSeries.getDateTimes();
		int ct = __inclCurrDay ? __period + 1 : __period;
		int currIdx = -1;
		boolean aligned = true;
		try {
			while (ct > 0) {  // 近 n 个交易日, 每个交易日一组数据: [baseDm, currDm]
				// 收集样本:
				ZonedDateTime aDm = allDms.get(allDms.size() + currIdx);
				double aVal = dataSeries.__getitem__(currIdx);
				currIdx += -1;
				if (aDm.toLocalTime().compareTo(dateTime.toLocalTime()) == 0) {
					aligned = true;
				}
				if (aligned) {
					allSampleDms.add(aDm);
					allSampleValues.add(aVal);
				}
				if (aDm.toLocalTime().compareTo(__baseTime) == 0) {
					ct--;
					aligned = false;
				}
			}
			// 计算均值, 标准差, n 倍标准差轨道:
			double mean = CommonUtils.mean(allSampleValues);
			double std = Math.sqrt(CommonUtils.variance(allSampleValues));
			double upperValue1 = mean + std * __numStdDev1;
			double lowerValue1 = mean - std * __numStdDev1;
			double upperValue2 = mean + std * __numStdDev2;
			double lowerValue2 = mean - std * __numStdDev2;
			double upperValue3 = mean + std * __numStdDev3;
			double lowerValue3 = mean - std * __numStdDev3;
			// 更新各个指标 dataseries:
			__mean.appendWithDateTime(dateTime, mean);
			__stdDev.appendWithDateTime(dateTime, std);
			__upperBand1.appendWithDateTime(dateTime, upperValue1);
			__lowerBand1.appendWithDateTime(dateTime, lowerValue1);
			__upperBand2.appendWithDateTime(dateTime, upperValue2);
			__lowerBand2.appendWithDateTime(dateTime, lowerValue2);
			__upperBand3.appendWithDateTime(dateTime, upperValue3);
			__lowerBand3.appendWithDateTime(dateTime, lowerValue3);

		} catch (IndexOutOfBoundsException e) {
			// 样本数据不足:
		}
	}

	public SequenceDataSeries<Double> getMean() {

		return __mean;
	}

	public SequenceDataSeries<Double> getStdDev() {

		return __stdDev;
	}

	public SequenceDataSeries<Double> getUpperBand1() {

		return __upperBand1;
	}

	public SequenceDataSeries<Double> getLowerBand1() {

		return __lowerBand1;
	}

	public SequenceDataSeries<Double> getUpperBand2() {

		return __upperBand2;
	}

	public SequenceDataSeries<Double> getLowerBand2() {

		return __lowerBand2;
	}

	public SequenceDataSeries<Double> getUpperBand3() {

		return __upperBand3;
	}

	public SequenceDataSeries<Double> getLowerBand3() {

		return __lowerBand3;
	}
}
